package com.eclipse.e20200202_数据库连接池工具包.com.qf.app1.utils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

/**
 * JDBC 的工具类
 * 
 * @author Administrator
 *
 */
public class JDBCUtils {
	private static Logger logger = LogManager.getLogger();

	private static final String CONFIG_FILENAME = "hikaricp.properties";

	private static Properties config = new Properties();

	private static DataSource dataSource;//数据源

	//初始化连接池
	static {
		try {
			logger.info("正在读取配置文件：" + CONFIG_FILENAME);
			config.load(JDBCUtils.class.getClassLoader().getResourceAsStream(CONFIG_FILENAME));//配置文件添加到properties对象中
		} catch (IOException e) {
			logger.fatal(e);
			throw new RuntimeException("读取数据源配置文件失败。。。。。");
		}
		dataSource = new HikariDataSource(new HikariConfig(config));//把数据源放入dataSource对象中
	}

	/**
	 * 执行insert、update、delete
	 * 
	 * @param sql
	 * @param params
	 */
	public static void update(String sql, Object... params) {
		Connection connection = getConnection();

		PreparedStatement statement = null;
		try {
			statement = connection.prepareStatement(sql);

			for (int i = 0; i < params.length; i++) {
				statement.setObject(i + 1, params[i]);
			}

			statement.executeUpdate();
		} catch (SQLException ex) {
			logger.warn(ex);
			throw new RuntimeException("执行的SQL有问题：" + sql, ex);
		} finally {
			closeResourec(connection, statement, null);
		}
	}

	/**
	 * 查询
	 * 
	 * @param sql
	 * @param clz
	 * @param params
	 * @return
	 */
	public static <E> List<E> select(String sql, Class<E> clz, Object... params) {//需要一个Class类对象 查询所有不需要参数
		Connection connection = getConnection();
		PreparedStatement statement = null;
		try {
			statement = connection.prepareStatement(sql);

			//给sql语句里的?放入值 避免sql注入
			for (int i = 0; i < params.length; i++) {
				statement.setObject(i + 1, params[i]);
			}
			ResultSet resultSet = statement.executeQuery();

			// 存放返回实体对象
			List<E> result = new ArrayList<>();
			E entity = null;
			Field[] fields = null;
			Object columnValue = null;
			
			while (resultSet.next()) {
				// 动态创建实体对象
				entity = clz.newInstance();
				result.add(entity);

				// 动态获取类的所有属性
				fields = clz.getDeclaredFields();//获取当前类中声明的所有属性，包括私有，返回Field类型数组

				// 遍历所有的属性，然后从结果集中获取同名的数据列的值
				for (Field field : fields) {
					columnValue = resultSet.getObject(field.getName());//从查询到的结果集resultSet 里获取 与传进来的类对象的属性名字一样的数据库列名
					// 把数据库列的值放到实体对象中
					field.setAccessible(true);//修改私有属性可见性
					field.set(entity, columnValue);// 属性.set(对象名,新的值)给哪个对象的这个属性赋值
				}
			}
			return result;
		} catch (Exception ex) {
			logger.warn(ex);
			throw new RuntimeException("执行的SQL有问题：" + sql, ex);
		} finally {
			closeResourec(connection, statement, null);
		}
	}

	/**
	 * 关闭资源
	 * 
	 * @param connection
	 * @param statement
	 * @param resultSet
	 */
	public static void closeResourec(Connection connection, Statement statement, ResultSet resultSet) {
		try {
			if (resultSet != null) {
				resultSet.close();
			}
			if (statement != null) {
				statement.close();
			}
			if (connection != null) {
				connection.close();
			}
		} catch (SQLException ex) {
			logger.warn(ex);
		}
	}

	/**
	 * 打开连接
	 * 
	 * @return
	 * @throws Exception
	 */
	public static Connection getConnection() {
		try {
			return dataSource.getConnection();
		} catch (SQLException ex) {
			logger.warn(ex);
			throw new RuntimeException("连接池中的连接被用完了，而且在指定时间内没有线程还连接", ex);
		}
	}
}
